package com.meilai.project.service.business.contract.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.meilai.project.controller.web.adMake.mapper.BillAdMakeMapper;
import com.meilai.project.controller.web.business.contract.dto.ContractApplySaveReq;
import com.meilai.project.controller.web.business.contract.dto.ContractAttachmentReq;
import com.meilai.project.controller.web.market.mapper.BillMarketMapper;
import com.meilai.project.dto.base.ApiResult;
import com.meilai.project.dto.business.contract.*;
import com.meilai.project.dto.workflow.FlowBusinessDataSuperEntity;
import com.meilai.project.dto.workflow.WorkFlowCheckDTO;
import com.meilai.project.entity.business.contract.*;
import com.meilai.project.entity.business.customer.CustomerInfo;
import com.meilai.project.entity.business.intentionScheme.IntentionScheme;
import com.meilai.project.entity.media.MediaPointChooseDaterange;
import com.meilai.project.entity.personnel.org.Department;
import com.meilai.project.exception.CommonException;
import com.meilai.project.mapper.business.contract.ContractApplyMapper;
import com.meilai.project.mapper.business.contract.ContractAttachmentMapper;
import com.meilai.project.mapper.business.contract.ContractMediaDetailMapper;
import com.meilai.project.mapper.business.contract.ContractPayDetailMapper;
import com.meilai.project.mapper.intentionScheme.IntentionSchemeMapper;
import com.meilai.project.mapper.media.MediaPointChooseDaterangeMapper;
import com.meilai.project.service.business.contract.*;
import com.meilai.project.service.business.customer.CustomerInfoService;
import com.meilai.project.service.business.intentionScheme.IntentionSchemeService;
import com.meilai.project.service.business.intentionScheme.PointProcessService;
import com.meilai.project.service.business.sale.BillSaleService;
import com.meilai.project.service.personnel.org.DepartmentService;
import com.meilai.project.service.system.impl.UserServiceImpl;
import com.meilai.project.util.ThreadLocalUserUtil;
import com.meilai.project.util.WorkFlowUtil;
import com.meilai.project.vo.business.contract.*;
import com.meilai.project.vo.business.intentionScheme.AmendContractCountVO;
import com.meilai.project.vo.system.UserForWFVO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author xchen
 * @since 2022-03-02
 */
@Service
public class ContractApplyServiceImpl extends ServiceImpl<ContractApplyMapper, ContractApply> implements ContractApplyService {

    @Autowired
    private IntentionSchemeMapper intentionSchemeMapper;
    @Autowired
    private PointProcessService processService;
    @Autowired
    private ContractAttachmentMapper contractAttachmentMapper;
    @Autowired
    private MediaPointChooseDaterangeMapper mediaPointChooseDaterangeMapper;
    @Autowired
    private ContractAttachmentService attachmentService;
    @Autowired
    private ProxyMediaDetailService proxyMediaDetailService;

    @Autowired
    private ContractPayDetailMapper contractPayDetailMapper;

    @Autowired
    private ContractPayDetailService contractPayDetailService;

    @Autowired
    private ContractMediaDetailMapper contractMediaDetailMapper;

    @Autowired
    private ContractMediaDetailService contractMediaDetailService;

    @Autowired
    private IntentionSchemeService intentionSchemeService;

    @Autowired
    private BillSaleService billSaleService;
    @Autowired
    private BillNumberService billNumberService;
    @Autowired
    private DepartmentService departmentService;

    @Autowired
    private CustomerInfoService customerInfoService;

    @Autowired
    private ContractMediaReplacePriceService contractMediaReplacePriceService;

    @Autowired
    private ContractProxyMediaReplacePriceService contractProxyMediaReplacePriceService;

    @Autowired
    private BillAdMakeMapper billAdMakeMapper;

    @Autowired
    private BillMarketMapper billMarketMapper;

    @Override
    public List<ContractApplyVO> listByRootContractId(Long root_id) {
        List<ContractApplyVO> contractApplyVOS = baseMapper.listByRootId(root_id);
        if (CollUtil.isEmpty(contractApplyVOS)) {
            return contractApplyVOS;
        }

        //处理最新的contract_id
        if (CollUtil.isNotEmpty(contractApplyVOS)) {
            //非原合同，取root_id
            List<Long> rootIds = contractApplyVOS.stream().map(ContractApplyVO::getRoot_id).distinct().collect(Collectors.toList());
            List<ContractApplyVO> latestVOList = baseMapper.listLatest(rootIds);
            Map<Long, ContractApplyVO> id2LatestMap = latestVOList.stream().collect(Collectors.toMap(ContractApplyVO::getId, Function.identity()));

            for (ContractApplyVO contractApplyVO : contractApplyVOS) {
                contractApplyVO.setFlow_json(null);
                Long contractId = contractApplyVO.getId();
                ContractApplyVO latestVO = id2LatestMap.get(contractId);
                if (null != latestVO) {
                    contractApplyVO.setLast_contract_id(latestVO.getId());
                }
            }
        }
        List<Long> ids = contractApplyVOS.stream().map(ContractApplyVO::getId).collect(Collectors.toList());
        List<AmendContractCountVO> amendContractCountVOS = intentionSchemeMapper.listByContractIds(ids);
        Map<Long, Integer> contractId2CountMap = amendContractCountVOS.stream().collect(Collectors.toMap(AmendContractCountVO::getContract_id, AmendContractCountVO::getType_count));
        for (ContractApplyVO contractApplyVO : contractApplyVOS) {
            contractApplyVO.setAmend_count(contractId2CountMap.get(contractApplyVO.getId()));
        }
        return contractApplyVOS;
    }

    @Override
    public List<NotCompleteSaleContractVO> listNotComplete(Long userId) {
        List<NotCompleteSaleContractVO> result = new ArrayList<>();
        //所有合同未转销售的chooseId和对应的daterange_id列表
        List<ContractNotCompleteChooseAndDateRangePO> notCompleteList = baseMapper.listNotCompleteContractChooseAndDateRange();
        if (CollUtil.isEmpty(notCompleteList)) {
            return result;
        }
        List<Long> chooseIdList = notCompleteList.stream().map(ContractNotCompleteChooseAndDateRangePO::getChoose_id).collect(Collectors.toList());
        List<ContractApplyVO> contractApplyVOS = baseMapper.listByChooseIdList(userId, chooseIdList);
        List<Long> dateRangeIdList = notCompleteList.stream().map(ContractNotCompleteChooseAndDateRangePO::getDaterange_id).collect(Collectors.toList());
        List<MediaPointChooseDaterange> dateRangeList = mediaPointChooseDaterangeMapper.listByDaterangeIdList(dateRangeIdList);
        Map<Long, List<MediaPointChooseDaterange>> chooseId2RangeMap = dateRangeList.stream().collect(Collectors.groupingBy(MediaPointChooseDaterange::getChoose_id));
        List<Long> idList = contractApplyVOS.stream().map(ContractApplyVO::getId).collect(Collectors.toList());
        Map<Long, List<ProxyMediaDetailReq>> proxyMediaMap = proxyMediaDetailService.queryByIds(1, idList);
        for (ContractApplyVO contractApplyVO : contractApplyVOS) {
            NotCompleteSaleContractVO vo = convert(contractApplyVO);
            vo.setProxyMediaDetailList(proxyMediaMap.getOrDefault(contractApplyVO.getId(), new ArrayList<>()));
            List<ContractDateRangeVO> dateRangeVOList = new ArrayList<>();
            vo.setDateRangeList(dateRangeVOList);
            List<MediaPointChooseDaterange> rangeList = chooseId2RangeMap.get(vo.getChooseId());
            if (CollUtil.isNotEmpty(rangeList)) {
                for (MediaPointChooseDaterange pointChooseDaterange : rangeList) {
                    ContractDateRangeVO rangeVO = new ContractDateRangeVO();
                    rangeVO.setDateRangeId(pointChooseDaterange.getId());
                    rangeVO.setBeginDate(pointChooseDaterange.getStart_at().toLocalDate());
                    rangeVO.setEndDate(pointChooseDaterange.getEnd_at().toLocalDate());
                    dateRangeVOList.add(rangeVO);
                }
                result.add(vo);
            }
        }


        return result;
    }

    private NotCompleteSaleContractVO convert(ContractApplyVO contractApplyVO) {
        NotCompleteSaleContractVO notCompleteSaleContractVO = new NotCompleteSaleContractVO();
        notCompleteSaleContractVO.setId(contractApplyVO.getId());
        notCompleteSaleContractVO.setContractName(contractApplyVO.getPart_a());
        notCompleteSaleContractVO.setContractAmount(contractApplyVO.getDiscounted_price());
        notCompleteSaleContractVO.setCustomerId(contractApplyVO.getCustomer_id());
        notCompleteSaleContractVO.setCustomerName(contractApplyVO.getCustomerName());
        notCompleteSaleContractVO.setContractBeginDate(contractApplyVO.getContract_begin_date());
        notCompleteSaleContractVO.setContractEndDate(contractApplyVO.getContract_end_date());
        return notCompleteSaleContractVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long generateContract(ContractGenerateDTO req) {
        List<IntentionScheme> intentionSchemes = intentionSchemeMapper.selectBatchIds(req.getIntention_scheme_id_list());
        //提交时，检查方案是否被解除了锁定，如果被解除了锁定则不能转为合同
        for (IntentionScheme intentionScheme : intentionSchemes) {
            if (null == intentionScheme.getLock_user_id()) {
                throw new CommonException("方案【" + intentionScheme.getPlan_name() + "】已经被转为合同，请重新选择！");
            }
        }

        List<Long> oldChooseIdList = intentionSchemes.stream().map(IntentionScheme::getChoose_id).collect(Collectors.toList());
        Long newChooseId = processService.copyChooseAndTransferLockPoint(2, oldChooseIdList);
        intentionSchemeMapper.releaseLock(oldChooseIdList);

        // 获取当前人的id和部门id
        UserForWFVO user = UserServiceImpl.getUserForWF(req);
        CustomerInfo customerInfo = customerInfoService.getById(req.getCustomer_id());
        ContractApply temp = new ContractApply();
        BeanUtils.copyProperties(req, temp);

        // 设置流程字段，flow_type请自行对应“dict_flow_type”表中的id， 比如当前入职流程（考察登记流程）的id是5
        String flow_title = "销售合同申请-" + customerInfo.getName(); // 审批流程的名称，需要根据实际业务自行确定
        FlowBusinessDataSuperEntity fbdse = WorkFlowUtil.getStartSuperDataEntity(user.getUser_id(),  // 当前用户id
                user.getDep_id(), // 提交的部门id
                17, // 流程类型id，“dict_flow_type”表中的id
                flow_title // 自定义的流程标题
        );
        BeanUtils.copyProperties(fbdse, temp);

        // 是否为新数据
        // 新数据时，前端不传业务数据id
        // 非新数据时，即来源是“草稿箱”、“已删除”、“被退回”时，前端需要传入业务数据id

        boolean flag = save(temp);
        if (!flag) throw new CommonException("保存失败");

        saveAttachment(req.getAttachmentList(), temp);

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<ContractApply, ContractApplyMapper>(temp.getId(), user.getUser_id(), ContractApply.class);
        Long wfData_id = 0L;
        wfu.SaveDraft(wfData_id);

        return temp.getId();
    }

    private void saveAttachment(List<ContractAttachmentReq> attachmentList, ContractApply temp) {
        contractAttachmentMapper.clearContractRelation(temp.getId());
        if (CollUtil.isNotEmpty(attachmentList)) {
            List<ContractAttachment> contractAttachmentList = new ArrayList<>();
            for (ContractAttachmentReq contractAttachmentReq : attachmentList) {
                ContractAttachment attachment = contractAttachmentReq.toPO();
                attachment.setType(1);
                attachment.setContract_id(temp.getId());
                contractAttachmentList.add(attachment);
            }
            attachmentService.saveBatch(contractAttachmentList);
        }
    }

    private void saveMediaDetail(List<MediaDetailReq> mediaDetailReqList, ContractApply temp) {
        contractMediaDetailMapper.clearContractRelation(temp.getId());
        if (CollUtil.isNotEmpty(mediaDetailReqList)) {
            List<ContractMediaDetail> mediaDetails = new ArrayList<>();
            for (MediaDetailReq mediaDetailReq : mediaDetailReqList) {
                ContractMediaDetail attachment = mediaDetailReq.toPO();
                attachment.setContract_id(temp.getId());
                mediaDetails.add(attachment);
            }
            contractMediaDetailService.saveBatch(mediaDetails);
        }
    }

    private void savePayDetail(List<PayDetailReq> payDetailReqs, ContractApply temp) {
        contractPayDetailMapper.clearContractRelation(temp.getId());
        if (CollUtil.isNotEmpty(payDetailReqs)) {
            List<ContractPayDetail> contractPayDetails = new ArrayList<>();
            for (PayDetailReq mediaDetailReq : payDetailReqs) {
                ContractPayDetail payDetail = mediaDetailReq.toPO();
                payDetail.setContract_id(temp.getId());
                contractPayDetails.add(payDetail);
            }
            contractPayDetailService.saveBatch(contractPayDetails);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ContractAttachment saveAttachment(ContractAttachmentReq contractAttachmentReq) {
        ContractAttachment entity = contractAttachmentReq.toPO();
        contractAttachmentMapper.insert(entity);
        return entity;
    }

    @Override
    public List<String> partAHistory(Long customerId) {
        return baseMapper.listPartAHistory(customerId);
    }

    @Override
    public Page<ContractApplyVO> selectPageList(ContractApplyListDTO searchDTO) {
        Page<ContractApplyVO> page = searchDTO.createPage(ContractApplyVO.class);
        List<ContractApplyVO> contractApplyVOS = baseMapper.selectPageList(page, searchDTO);
        if (CollUtil.isNotEmpty(contractApplyVOS)) {
            //非原合同，取root_id
            List<Long> rootIds = contractApplyVOS.stream().map(ContractApplyVO::getId).distinct().collect(Collectors.toList());
            List<ContractChildCountVO> subCountList = baseMapper.listSubCount(rootIds);
            List<AmendContractCountVO> amendContractCountVOS = intentionSchemeMapper.listByContractIds(rootIds);
            Map<Long, Integer> contractId2CountMap = amendContractCountVOS.stream().collect(Collectors.toMap(AmendContractCountVO::getContract_id, AmendContractCountVO::getType_count));
            List<ContractApplyVO> latestVOList = baseMapper.listLatest(rootIds);
            List<ContractApplyVO> latestChildVOList = baseMapper.listLatestChild(rootIds);
            Map<Long, Integer> rootId2CountMap = subCountList.stream().collect(Collectors.toMap(ContractChildCountVO::getId, ContractChildCountVO::getSub_count));
            Map<Long, ContractApplyVO> rootId2LatestMap = latestVOList.stream().collect(Collectors.toMap(ContractApplyVO::getRoot_id, Function.identity()));
            Map<Long, ContractApplyVO> rootId2LatestChildMap = latestChildVOList.stream().collect(Collectors.toMap(ContractApplyVO::getRoot_id, Function.identity()));

            List<Long> lastestIds = latestChildVOList.stream().map(ContractApplyVO::getId).collect(Collectors.toList());
            // todo 增加合同的代理资源和自有资源价格明细数据
            Set<Long> ids = new HashSet<>(rootIds);
            ids.addAll(lastestIds);
            List<ContractMediaPrice> mediaPrices = baseMapper.queryMediaDetailBysIds(ids);
            // 自有资源map
            Map<Long, List<ContractMediaPrice>> my_media_detail_map = mediaPrices.stream().filter(item -> item.getType() == 1).collect(Collectors.groupingBy(ContractMediaPrice::getId));
            Map<Long, List<ContractMediaPrice>> proxy_media_detail_map = mediaPrices.stream().filter(item -> item.getType() == 2).collect(Collectors.groupingBy(ContractMediaPrice::getId));
            Map<Long, String> my_media_names_map = mediaPrices.stream().filter(item -> item.getType() == 1).distinct().collect(Collectors.groupingBy(ContractMediaPrice::getId, Collectors.mapping(ContractMediaPrice::getMedia_type_name, Collectors.joining(","))));
            Map<Long, String> proxy_media_names_map = mediaPrices.stream().filter(item -> item.getType() == 2).distinct().collect(Collectors.groupingBy(ContractMediaPrice::getId, Collectors.mapping(ContractMediaPrice::getMedia_type_name, Collectors.joining(","))));

            // 自有资源和代理资源
            List<ContractMediaCountDTO> mediaCounts = baseMapper.getMediaTypeCount(rootIds);
            Map<Long, List<ContractMediaCountDTO>> mediaCountMap = new HashMap<>();
            Map<Long, List<ContractMediaCountDTO>> proxyMediaCountMap = new HashMap<>();
            if (CollectionUtils.isNotEmpty(mediaCounts)) {
                mediaCountMap = mediaCounts.stream().filter(item -> item.getType() == 1).collect(Collectors.groupingBy(ContractMediaCountDTO::getId));
                proxyMediaCountMap = mediaCounts.stream().filter(item -> item.getType() == 2).collect(Collectors.groupingBy(ContractMediaCountDTO::getId));
            }

            for (ContractApplyVO contractApplyVO : contractApplyVOS) {
                contractApplyVO.setFlow_json(null);
                Long rootId = contractApplyVO.getId();
                contractApplyVO.setAmend_count(contractId2CountMap.get(rootId));
                contractApplyVO.setChild_contract_count(rootId2CountMap.get(rootId));
                ContractApplyVO latestVO = rootId2LatestMap.get(rootId);
                Long curVOId = null;
                if (null != latestVO) {
                    ContractApplyVO childContractApplyVO = rootId2LatestChildMap.get(rootId);
                    if (null != childContractApplyVO) {
                        contractApplyVO.setLast_contract_follow_status(childContractApplyVO.getFlow_status());
                    }
                    contractApplyVO.setLast_contract_id(latestVO.getId());
                    //签订日期 合同时间 优惠价格 优惠价格大写 更换画面价格 甲方联系人 和联系方式 广告发布费 广发发布费大写其他约定
                    contractApplyVO.setSign_date(latestVO.getSign_date());
                    contractApplyVO.setContract_begin_date(latestVO.getContract_begin_date());
                    contractApplyVO.setContract_end_date(latestVO.getContract_end_date());
                    contractApplyVO.setDiscounted_price(latestVO.getDiscounted_price());
                    contractApplyVO.setDiscounted_price_big(latestVO.getDiscounted_price_big());
                    contractApplyVO.setReplace_price(latestVO.getReplace_price());
                    contractApplyVO.setMulti_replace_price(latestVO.getMulti_replace_price());
                    contractApplyVO.setAcceptance_way(latestVO.getAcceptance_way());
                    contractApplyVO.setPart_a(latestVO.getPart_a());
                    contractApplyVO.setPart_a_contact(latestVO.getPart_a_contact());
                    contractApplyVO.setMemo(latestVO.getMemo());
                    curVOId = latestVO.getId();

                } else {
                    curVOId = contractApplyVO.getId();
                }
                // 更换为最新的合同详情
                contractApplyVO.setContractMediaPriceList(my_media_detail_map.getOrDefault(curVOId, new ArrayList<>()));
                contractApplyVO.setContractMediaPriceProxyList(proxy_media_detail_map.getOrDefault(curVOId, new ArrayList<>()));
                contractApplyVO.setMedia_type_names(my_media_names_map.getOrDefault(curVOId, ""));
                contractApplyVO.setProxy_media_type_names(proxy_media_names_map.getOrDefault(curVOId, ""));
                // 资源类型
                List<ContractMediaCountDTO> mcs = mediaCountMap.getOrDefault(contractApplyVO.getId(), new ArrayList<>());
                contractApplyVO.setMediaCountList(mcs);
                List<ContractMediaCountDTO> pmcs = proxyMediaCountMap.getOrDefault(contractApplyVO.getId(), new ArrayList<>());
                contractApplyVO.setProxyMediaCountList(pmcs);
            }
        }
        page.setRecords(contractApplyVOS);
        return page;
    }

    @Override
    public Page<ContractApplyVO> selectLatestPageList(ContractApplyLatestListDTO searchDTO) {
        Page<ContractApplyVO> page = searchDTO.createPage(ContractApplyVO.class);
        List<ContractApplyVO> contractApplyVOS = baseMapper.listPageLatest(page, searchDTO);
        page.setRecords(contractApplyVOS);
        if (CollectionUtils.isNotEmpty(contractApplyVOS)) {
            List<Long> rootIds = contractApplyVOS.stream().map(ContractApplyVO::getId).distinct().collect(Collectors.toList());
            List<ContractMediaCountDTO> mediaCounts = baseMapper.getMediaTypeCount(rootIds);
            Map<Long, List<ContractMediaCountDTO>> mediaCountMap = new HashMap<>();
            Map<Long, List<ContractMediaCountDTO>> proxyMediaCountMap = new HashMap<>();
            if (CollectionUtils.isNotEmpty(mediaCounts)) {
                mediaCountMap = mediaCounts.stream().filter(item -> item.getType() == 1).collect(Collectors.groupingBy(ContractMediaCountDTO::getId));
                proxyMediaCountMap = mediaCounts.stream().filter(item -> item.getType() == 2).collect(Collectors.groupingBy(ContractMediaCountDTO::getId));
            }
            for (ContractApplyVO contractApplyVO : contractApplyVOS) {
                // 资源类型
                List<ContractMediaCountDTO> mcs = mediaCountMap.getOrDefault(contractApplyVO.getId(), new ArrayList<>());
                contractApplyVO.setMediaCountList(mcs);
                List<ContractMediaCountDTO> pmcs = proxyMediaCountMap.getOrDefault(contractApplyVO.getId(), new ArrayList<>());
                contractApplyVO.setProxyMediaCountList(pmcs);
            }
        }
        return page;
    }

    @Override
    public List<ContractApplyVO> listByRootId(Long rootId) {
        return null;
    }

    @Override
    public List<ContractApplyVO> listLatestByCustomerId(Long customerId) {
        List<ContractApplyVO> contractApplyVOS = baseMapper.listLatestByCustomerId(customerId);
        if (CollectionUtils.isNotEmpty(contractApplyVOS)) {

            List<Long> rootIds = contractApplyVOS.stream().map(ContractApplyVO::getId).distinct().collect(Collectors.toList());
            List<ContractMediaCountDTO> mediaCounts = baseMapper.getMediaTypeCount(rootIds);
            Map<Long, List<ContractMediaCountDTO>> mediaCountMap = new HashMap<>();
            Map<Long, List<ContractMediaCountDTO>> proxyMediaCountMap = new HashMap<>();
            if (CollectionUtils.isNotEmpty(mediaCounts)) {
                mediaCountMap = mediaCounts.stream().filter(item -> item.getType() == 1).collect(Collectors.groupingBy(ContractMediaCountDTO::getId));
                proxyMediaCountMap = mediaCounts.stream().filter(item -> item.getType() == 2).collect(Collectors.groupingBy(ContractMediaCountDTO::getId));
            }

            List<ContractMediaPrice> mediaPrices = baseMapper.queryMediaDetailBysIds(contractApplyVOS.stream().map(ContractApply::getId).collect(Collectors.toSet()));
            Map<Long, List<ContractMediaPrice>> my_media_detail_map = mediaPrices.stream().filter(item -> item.getType() == 1).collect(Collectors.groupingBy(ContractMediaPrice::getId));
            Map<Long, List<ContractMediaPrice>> proxy_media_detail_map = mediaPrices.stream().filter(item -> item.getType() == 2).collect(Collectors.groupingBy(ContractMediaPrice::getId));

            for (ContractApplyVO contractApplyVO : contractApplyVOS) {
                // 资源类型
                List<ContractMediaCountDTO> mcs = mediaCountMap.getOrDefault(contractApplyVO.getId(), new ArrayList<>());
                contractApplyVO.setMediaCountList(mcs);
                List<ContractMediaCountDTO> pmcs = proxyMediaCountMap.getOrDefault(contractApplyVO.getId(), new ArrayList<>());
                contractApplyVO.setProxyMediaCountList(pmcs);

                contractApplyVO.setContractMediaPriceList(my_media_detail_map.getOrDefault(contractApplyVO.getId(), new ArrayList<>()));
                contractApplyVO.setContractMediaPriceProxyList(proxy_media_detail_map.getOrDefault(contractApplyVO.getId(), new ArrayList<>()));
            }
        }
        return contractApplyVOS.stream().sorted(Comparator.comparing(ContractApplyVO::getContract_begin_date, Comparator.nullsFirst(LocalDate::compareTo))).collect(Collectors.toList());
    }

    @Override
    public List<ContractApplyVO> listForMarketByCustomerId(Long customerId, Long mustIncludeContractId) {
        return baseMapper.listForMarketByCustomerId(customerId, mustIncludeContractId);
    }

    @Override
    public List<ContractApplyForADMakeVO> listForADMakeByDeptId(Long customer_id, Long mustIncludeContractId) {
        return baseMapper.listForADMakeByDeptId(customer_id, mustIncludeContractId);
    }

    @Override
    public Page<ContractApplyVO> selectPageAmendList(ContractApplyAmendListDTO searchDTO) {
        Page<ContractApplyVO> page = searchDTO.createPage(ContractApplyVO.class);
        List<ContractApplyVO> contractApplyVOS = baseMapper.selectPageAmendList(page, searchDTO);
        if (CollUtil.isNotEmpty(contractApplyVOS)) {
            for (ContractApplyVO contractApplyVO : contractApplyVOS) {
                contractApplyVO.setFlow_json(null);
            }
        }
        page.setRecords(contractApplyVOS);
        return page;
    }

    @Override
    public ContractApplyVO getOneById(Long id) {
        ContractApplyVO result = baseMapper.queryDetailById(id);
        if (result == null) throw new CommonException("合同不存在！");
        // 当前用户ID
        Long userId = ThreadLocalUserUtil.get().getId();
        // 判断是否当前人维护
        if(result.getMaintenance_staff_id() != null) result.setCur_user_maintenance(userId.equals(result.getMaintenance_staff_id()));
        // 判断是否可以审批
        result.setCan_check(WorkFlowUtil.canCheckData(userId, result));
        // 获取合同附件
        List<ContractAttachment> contractAttachments = contractAttachmentMapper.listByContractId(1, id);
        result.setAttachmentList(contractAttachments);
        // 获取合同付款方式详情
        LambdaQueryWrapper<ContractPayDetail> payDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
        payDetailLambdaQueryWrapper.eq(ContractPayDetail::getContract_id, id);
        result.setPayDetailList(contractPayDetailService.list(payDetailLambdaQueryWrapper));

        // 获取合同自营媒体详情
        LambdaQueryWrapper<ContractMediaDetail> mediaDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
        mediaDetailLambdaQueryWrapper.eq(ContractMediaDetail::getContract_id, id);
        result.setMediaDetailList(contractMediaDetailService.list(mediaDetailLambdaQueryWrapper));

        // 获取合同代理媒体详情
        List<ProxyMediaDetailReq> proxyMediaDetailReqList = proxyMediaDetailService.query(1, id);
        result.setProxyMediaDetailList(proxyMediaDetailReqList);

        // 判断是否是旧数据
        LocalDateTime time = LocalDateTime.of(2022, 9, 21, 14, 0, 0);
        result.setOld_data(result.getCreated_at().isBefore(time));

        // 获取合同自营媒体换画金额
        List<ContractMediaReplacePrice> contractMediaReplacePriceList = contractMediaReplacePriceService.list(Wrappers.<ContractMediaReplacePrice>lambdaQuery().eq(ContractMediaReplacePrice::getContract_id, result.getId()));
        result.setContractMediaReplacePriceList(contractMediaReplacePriceList);

        // 获取合同代理媒体换画金额
        List<ContractProxyMediaReplacePrice> contractProxyMediaReplacePriceList = contractProxyMediaReplacePriceService.list(Wrappers.<ContractProxyMediaReplacePrice>lambdaQuery().eq(ContractProxyMediaReplacePrice::getContract_id, result.getId()));
        result.setContractProxyMediaReplacePriceList(contractProxyMediaReplacePriceList);

        // 当前合同已累计执行金额
        result.setTotalExecutedAmount(billSaleService.getTotalExecutedAmount(id));
        result.setTotalSelfExecutedAmount(new BigDecimal(0));
        result.setTotalProxyExecutedAmount(new BigDecimal(0));

        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveAttachment(ContractApplySaveReq req) {
        if(req.getId() == null) throw new CommonException("无合同id");
        if(CollectionUtils.isEmpty(req.getAttachmentList())) throw new CommonException("附件为空");
        ContractApply temp = new ContractApply();
        temp.setId(req.getId());
        saveAttachment(req.getAttachmentList(), temp);
        return true;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public ApiResult<Boolean> save(ContractApplySaveReq dto) {
        int flowResult = saveAndBegin(dto, true);
        if (flowResult != 1) throw new CommonException("保存至草稿箱失败，请联系管理员！");
        else return null;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public ApiResult<Boolean> begin(ContractApplySaveReq dto) {
        // 提交
        int flowResult = saveAndBegin(dto, false);
        if (flowResult == 1) {
            // 仍在流程中
            return null;
        } else if (flowResult == 2) {
            // 无审批节点，直接结束
            endProcess(dto.getId());
            return null;
        } else {
            throw new CommonException("提交失败，请联系管理员！");
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean pass(ContractApplyCheckDTO dto) {
        // 通过
        int flowResult = passAndBack(dto, true, false);
        if (flowResult == 1) {
            // 通过后仍然在流程中
            return true;
        } else if (flowResult == 2) {
            // 审核后流程结束
            // 更新roster表
            endProcess(dto.getId());

            return true;
        } else {
            throw new CommonException("通过失败，请联系管理员！");
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean returnBack(ContractApplyCheckDTO dto) {
        // 驳回
        int flowResult = passAndBack(dto, false, false);
        if (flowResult != 1) throw new CommonException("驳回失败，请联系管理员！");
        return true;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean delete(Long id) {
        int flowResult = deleteAndActualDel(id, false);
        if (flowResult != 1) throw new CommonException("删除失败，请联系管理员！");
        return true;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean actualDelete(Long id) {
        int flowResult = deleteAndActualDel(id, true);
        if (flowResult != 1) throw new CommonException("彻底删除失败，请联系管理员！");
        return true;
    }

    @Override
    public void hangUp(List<Long> chooseIdList) {
        baseMapper.updateMode(chooseIdList, 1);
    }

    @Override
    public void cancelHangUp(List<Long> chooseIdList) {
        baseMapper.updateMode(chooseIdList, 0);
    }

    @Override
    public void invalidByChooseIds(List<Long> chooseIdList) {
        baseMapper.updateMode(chooseIdList, 2);
    }

    public int saveAndBegin(ContractApplySaveReq dto, boolean isSave) {
        if (null != dto.getParent_id() && dto.getId() == null) {
            List<ContractApplyVO> contractApplyVOS = baseMapper.listByParentId(dto.getParent_id());
            if (CollUtil.isNotEmpty(contractApplyVOS)) {
                throw new CommonException("已存在补充协议,不能重复添加！");
            }
        }
        // 获取当前人的id和部门id
        UserForWFVO user = UserServiceImpl.getUserForWF(dto);
        ContractApply temp = new ContractApply();
        BeanUtils.copyProperties(dto, temp);
        CustomerInfo customerInfo = customerInfoService.getById(dto.getCustomer_id());
        // 设置流程字段，flow_type请自行对应“dict_flow_type”表中的id， 比如当前入职流程（考察登记流程）的id是5
        String flow_title = "销售合同申请-" + customerInfo.getName(); // 审批流程的名称，需要根据实际业务自行确定
        FlowBusinessDataSuperEntity fbdse = WorkFlowUtil.getStartSuperDataEntity(user.getUser_id(),  // 当前用户id
                user.getDep_id(), // 提交的部门id
                17, // 流程类型id，“dict_flow_type”表中的id
                flow_title // 自定义的流程标题
        );
        BeanUtils.copyProperties(fbdse, temp);

        // 是否为新数据
        // 新数据时，前端不传业务数据id
        // 非新数据时，即来源是“草稿箱”、“已删除”、“被退回”时，前端需要传入业务数据id
        boolean isFirst = (dto.getId() == null);

        // 由于流程字段含有id，会覆盖数据，此处需要手动setId
        if (!isFirst) temp.setId(dto.getId());

        //对于补充协议，找到其root_id
        Long parent_id = temp.getParent_id();
        if (null != parent_id) {
            ContractApply contractApply = baseMapper.selectById(parent_id);
            contractApply.setMode(1);
            updateById(contractApply);
            Long p_root_id = contractApply.getRoot_id();
            if (p_root_id == null) {
                p_root_id = contractApply.getId();
            }
            temp.setRoot_id(p_root_id);
        }

        if (StrUtil.isEmpty(dto.getContract_num()) || "提交后自动生成合同编号".equals(dto.getContract_num())) {
            Long companyId = dto.getPart_b_id();
            Department department = departmentService.getById(companyId);
            String contractNum = billNumberService.getNewContractNum(department.getContract_prefix());
            temp.setContract_num(contractNum);
        }

        boolean flag = isFirst ? save(temp) : updateById(temp);
        if (!flag) throw new CommonException("保存失败");

        Long entityId = temp.getId();
        //清理之前合同和附件的关联关系
        saveAttachment(dto.getAttachmentList(), temp);
        saveMediaDetail(dto.getMediaDetailList(), temp);
        savePayDetail(dto.getPayDetailList(), temp);
        proxyMediaDetailService.saveDetails(1, entityId, dto.getProxyMediaDetailList());

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<ContractApply, ContractApplyMapper>(entityId, user.getUser_id(), ContractApply.class);

        // 审批流程id
        // 新数据,传0L
        // 非新数据,根据数据id,查找wfData_id
        Long wfData_id = isFirst ? 0L : wfu.getWfDataId(dto.getId());
        // 旧数据记录留存
        ContractApply old_data = isFirst ? new ContractApply() : temp;
        // todo 保存媒体类型的子表数据
        contractMediaReplacePriceService.remove(Wrappers.<ContractMediaReplacePrice>lambdaQuery().eq(ContractMediaReplacePrice::getContract_id, temp.getId()));
        contractMediaReplacePriceService.saveBatch(dto.getContractMediaReplacePriceList().stream().map(item -> {
            ContractMediaReplacePrice contractMediaReplacePrice = new ContractMediaReplacePrice();
            BeanUtils.copyProperties(item, contractMediaReplacePrice);
            contractMediaReplacePrice.setContract_id(temp.getId());
            return contractMediaReplacePrice;
        }).collect(Collectors.toList()));
        contractProxyMediaReplacePriceService.remove(Wrappers.<ContractProxyMediaReplacePrice>lambdaQuery().eq(ContractProxyMediaReplacePrice::getContract_id, temp.getId()));
        contractProxyMediaReplacePriceService.saveBatch(
                dto.getContractProxyMediaReplacePriceList().stream().map(item -> {
                    ContractProxyMediaReplacePrice contractProxyMediaReplacePrice = new ContractProxyMediaReplacePrice();
                    BeanUtils.copyProperties(item, contractProxyMediaReplacePrice);
                    contractProxyMediaReplacePrice.setContract_id(temp.getId());
                    return contractProxyMediaReplacePrice;
                }).collect(Collectors.toList())
        );
        return isSave ? wfu.SaveDraft(wfData_id) : wfu.Begin(wfData_id, "", old_data);
    }

    // 通过 和 驳回 抽出相同代码
    private int passAndBack(ContractApplyCheckDTO dto, boolean isPass, boolean... updateData) {
        // 当前人id
        Long userId = ThreadLocalUserUtil.get().getId();

        // 业务数据
        ContractApply temp = getById(dto.getId());

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<ContractApply, ContractApplyMapper>(dto.getId(), userId, ContractApply.class);

        // 审批入参类
        WorkFlowCheckDTO checkDTO = WorkFlowCheckDTO.getWorkFlowCheckDTO(temp.getFlow_current_node_id(), // 当前节点id
                temp.getFlow_json(), // 流程json
                dto.getWf_check_opinion(), // 审批意见
                dto.getWf_check_sign_url(), // 审批签名链接
                JSONObject.toJSONString(temp) // 旧数据留存
        );

        // 审批
        return isPass ? wfu.ToNext(checkDTO) : wfu.ReturnBack(checkDTO);
    }

    //在合同审批结束后作废父合同
    public void endProcess(Long id) {
        ContractApply contractApply = getById(id);
        Long parent_id = contractApply.getParent_id();
        if (null != parent_id) {
            ContractApply parent = getById(parent_id);
            parent.setMode(2);
            updateById(parent);
            intentionSchemeService.updateContractId(parent_id, id);
            billSaleService.update(parent_id, id);
            billMarketMapper.updateContractId(parent_id, id);
            billAdMakeMapper.updateContractId(parent_id, id);
        }
    }

    // 删除 和 彻底删除 抽出相同代码
    private int deleteAndActualDel(Long id, boolean isActual) {
        // 当前人id
        Long userId = ThreadLocalUserUtil.get().getId();

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<ContractApply, ContractApplyMapper>(id, userId, ContractApply.class);

        // 流程id
        Long wfData_id = wfu.getWfDataId(id);
        //释放锁定
        ContractApply contractApply = baseMapper.selectById(id);
        //todo 确认 processService.releaseLock(contractApply.getChoose_id());

        return isActual ? wfu.ActualDelete(wfData_id) : wfu.Delete(wfData_id);
    }

    @Override
    public List<ContractApplyVO> list_by_customer_id(Long customerId) {
        return baseMapper.list_by_customer_id(customerId);
    }

    @Override
    public List<ContractApplyVO> getListByIds(List<Long> ids) {
        List<ContractApplyVO> contractApplyVOS = baseMapper.queryDetailByIds(ids);
        LambdaQueryWrapper<ContractPayDetail> payDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
        payDetailLambdaQueryWrapper.in(ContractPayDetail::getContract_id, ids);
        Map<Long, List<ContractPayDetail>> payDetailMap = contractPayDetailService.list(payDetailLambdaQueryWrapper).stream().collect(Collectors.groupingBy(ContractPayDetail::getContract_id));

        LambdaQueryWrapper<ContractMediaDetail> mediaDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
        mediaDetailLambdaQueryWrapper.in(ContractMediaDetail::getContract_id, ids);
        Map<Long, List<ContractMediaDetail>> mediaDetailMap = contractMediaDetailService.list(mediaDetailLambdaQueryWrapper).stream().collect(Collectors.groupingBy(ContractMediaDetail::getContract_id));
        Map<Long, List<ProxyMediaDetailReq>> proxyMediaDetailMap = proxyMediaDetailService.queryByIds(1, ids);
        // todo 查询媒体类型对应的价格数据
        Map<Long, List<ContractMediaReplacePrice>> contractMediaReplacePriceMap = contractMediaReplacePriceService.list(Wrappers.<ContractMediaReplacePrice>lambdaQuery().in(ContractMediaReplacePrice::getContract_id, ids)).stream().collect(Collectors.groupingBy(ContractMediaReplacePrice::getContract_id));
        Map<Long, List<ContractProxyMediaReplacePrice>> contractProxyMediaReplacePriceMap = contractProxyMediaReplacePriceService.list(Wrappers.<ContractProxyMediaReplacePrice>lambdaQuery().in(ContractProxyMediaReplacePrice::getContract_id, ids)).stream().collect(Collectors.groupingBy(ContractProxyMediaReplacePrice::getContract_id));
        contractApplyVOS.forEach(item -> {
            List defaultValue = new ArrayList<>();
            item.setPayDetailList(payDetailMap.getOrDefault(item.getId(), defaultValue));
            item.setContractMediaPriceList(mediaDetailMap.getOrDefault(item.getId(), defaultValue));
            item.setProxyMediaDetailList(proxyMediaDetailMap.getOrDefault(item.getId(), defaultValue));
            item.setContractMediaReplacePriceList(contractMediaReplacePriceMap.getOrDefault(item.getId(), defaultValue));
            item.setContractMediaPriceProxyList(contractProxyMediaReplacePriceMap.getOrDefault(item.getId(), defaultValue));
        });
        return contractApplyVOS;
    }

    @Override
    public List<ContractApply> get_contract_list_by_reimburse() {
        Long user_id = ThreadLocalUserUtil.get().getId();
        return baseMapper.query_contract_list_by_reimburse(user_id);
    }
}
